/*
 * Team Name: Focus
 * Team Members: Diana Groun, Edward Bassan
 * File Name: debug.c
 * Description:
 */
#include <stdio.h>
#include <stdlib.h>
#include "debug.h"
#include "global.h"
#include"cpu.h"
#include "controller.h"
#include<string.h>
#include <stdio.h>
#include <stdlib.h>



// Constructor
debugPtr newDebug(void) {
	debugPtr debug = (debugPtr) malloc(sizeof(debugStr));
	debug->controller = newController();
	debug->loadProgram = loadProgram;
	debug->runProgram = runProgram;
	debug->exitProgram = exitProgram;
	debug->displayMenu = displayMenu;
	debug->showUpdate = showUpdate;
	debug->stepProgram = stepProgram;
	debug->dumpMemory = dumpMemory;
	debug->showRegisters = showRegisters;
	debug->showMemory = showMemory;
	debug->showAll = showAll;
	return debug;
}

/*
 * Asks for a file to load into memory
 */
int loadProgram(debugPtr this) {
	printf("\tLoad Program\n");
	printf("\tPlease enter a filename: ");

	char* filename = (char*) malloc(sizeof(char) * 31);

	scanf("%s", filename);

	FILE *fsource;
	fsource = fopen(filename, "r");
	if (fsource == NULL ) {
		printf("Null File");
		return 2;
	}

	unsigned short instr;
	int index = 0;
	if (fsource != NULL ) {
		while (1) {
			if (feof(fsource))
				break;
			fscanf(fsource, "%hX", &instr);
			this->controller->memoryBlock->storeWord(
					this->controller->memoryBlock, index + 0x3000, instr);
			index = index + 2;

		}
	}
	fclose(fsource);

	showUpdate(this, 0, index);

	return NO_ERROR;
}

/*
 * runs the program currently in memory
 */
int runProgram(debugPtr this) {
	this->controller->startCycle(this->controller);
	this->showUpdate(this, 0, 250);
	return 0;
}

/*
 * Runs program instruction by instruction pausing for user input after each instruction
 */
int stepProgram(debugPtr this) {

	int state = 0;
	while (state < 6) {
		if(state == 0){
			printf("\tPress Enter to Continue: \n");
			getchar();
			this->showUpdate(this, 0, 200);
		}
		this->controller->switchCycle(this->controller, &state);
	}
	return NO_ERROR;
}

/*
 *If the user selects the dump memory command the monitor
 *If should prompt for the start address in hex of the dump.
 */
int dumpProgram(debugPtr this) {
	unsigned short start = 0;
	printf("\tPlease enter the start address: ");
	scanf("%hX", &start);
	printf("Memory\n");
	this->controller->memoryBlock->dispalyMemoryLocation(this->controller->memoryBlock, start ,0x3094);
	return NO_ERROR;
}

/*
 * Exits the program
 */
int exitProgram(debugPtr this, int **exit) {
	printf("\tExit Program\n");
	**exit = 1;
	return NO_ERROR;
}

/*
 * Displays options for a user to choose from
 */
int displayMenu(debugPtr this, int *exit) {
	int option = 0;
	showUpdate(this, 0, 200);

	printf(
			"\n------------------------------------------------------------------------------------\n");

	while (*exit == 0) {
		printf("Command: 1) load program, 2) run program, 3) step program, 4) dump memory,\n"
				"5) show memory, 6) show registers, 7) show all, 8) Exit\n");
		printf("\nPlease choose the Operation: ");
		scanf("%d", &option);
		getOperation(option, this, &exit);
		printf("\n");
	}

	return NO_ERROR;
}
/*
 * Displays all the registers and the memory that has been effected
 */
int showUpdate(debugPtr this, int mem_start, int mem_end) {
	printf("Registers\t\tMemory\n");
	int i = 0;
	int mem_i = 0;
	int max_index = mem_end;
	if (mem_end < 16) {
		max_index = 32;
	}
	while (mem_i < max_index) {
		if (i < 16) {
			printf("$R%X: %.4X", i,
					this->controller->cpu->registers->registers[i]->value);
		}
		if (i >= 16) {
			printf("\t");
		}
		printf("\t\t%.4X: %.2X %.2X\n", mem_i + MEMORY_OFFSET,
				this->controller->memoryBlock->fetchByte(
						this->controller->memoryBlock, mem_i + MEMORY_OFFSET),
						this->controller->memoryBlock->fetchByte(
								this->controller->memoryBlock,
								mem_i + MEMORY_OFFSET + 1));
		i++;
		mem_i = mem_i + 2;
	}

	i = 0;
	while (i < 50) {
		printf("\t\t\t%.4X: %.2X %.2X\n", i + STACK_OFFSET,
				this->controller->memoryBlock->stack[i],
				this->controller->memoryBlock->stack[i + 1]);
		i = i+2;
	}

	printf("\n");
	this->controller->cpu->displayCpuRegisters(this->controller->cpu);
	return NO_ERROR;
}
/**
 * Shows the contents of the registers.
 */
int showRegisters(debugPtr this)
{
	printf("Registers\n");
	int i;
	for(i = 0; i < REGISTER_FILE_SIZE; i++) {
		printf("$R%X: %.4X\n",i,this->controller->cpu->registers->registers[i]->value);
	}
	return NO_ERROR;
}
/**
 * Shows the contents of the memory.
 */
int showMemory(debugPtr this)
{
	printf("Memory\n");
	int i;
	for (i = 0; i < 150; i = i + 2) {
		printf("%.4X: %.2X %.2X\n",i+MEMORY_OFFSET,this->controller->memoryBlock->
				fetchByte(this->controller->memoryBlock, i+MEMORY_OFFSET),
				this->controller->memoryBlock->fetchByte(this->controller->memoryBlock,i+MEMORY_OFFSET + 1));
	}
	return NO_ERROR;
}
/**
 * Show all the updates for the memory and the registers
 */
int showAll(debugPtr this)
{
	showUpdate(this,0,200);
	return NO_ERROR;
}
/**
 * If the user selects the dump memory command the monitor should prompt
 *  for the start address in hex of the dump.
 */
int dumpMemory(debugPtr this)
{
	unsigned short start = 0;
	printf("\tPlease enter the start address: ");
	scanf("%hX", &start);
	printf("Memory\n");
	this->controller->memoryBlock->dispalyMemoryLocation(this->controller->memoryBlock, start ,0x3094);
	return NO_ERROR;
}

/*
 * Branches to the correct operations according to the given value
 */
int getOperation(int operation, debugPtr this, int **exit) {
	printf("Operation: %d\n", operation);

	switch (operation) {
	case 1:
		this->loadProgram(this);
		break;
	case 2:
		this->runProgram(this);
		break;
	case 3:
		this->stepProgram(this);
		break;
	case 4:
		this->dumpMemory(this);
		break;
	case 5:
		this->showMemory(this);
		break;
	case 6:
		this->showRegisters(this);
		break;
	case 7:
		this->showAll(this);
		break;
	case 8:
		this->exitProgram(this, &(*exit));
		break;
	}
	return NO_ERROR;
}
/*
 * Main method
 */

int main() {
	int exit = 0;

	debugPtr debug = newDebug();
	debug->displayMenu(debug, &exit);
	return 0;
}

